package de.slothsoft.commons.swing;

import static de.slothsoft.commons.swing.util.ControlUtil.createLabel;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import de.slothsoft.commons.res.CommonMessages;
import de.slothsoft.commons.swing.databinding.DataBinding;
import de.slothsoft.commons.swing.util.ErrorUtil;
import de.slothsoft.commons.swing.util.GridBagUtil;
import de.slothsoft.commons.swing.validation.ValidationBinding;

public class LoginPanel extends AbstractDataBindingPanel<LoginPanel.User> {

	private static final long serialVersionUID = 1L;

	private static final String CARD_LOGIN = "login";
	private static final String CARD_AFTER = "after";

	private JComponent loginComponent;
	private JComponent afterLoginComponent;
	private LoginProvider loginProvider;

	private CardLayout layout;

	protected JTextField txtPassword;

	public LoginPanel(JComponent afterLoginComponent,
			LoginProvider loginProvider) {
		this(createDefaultUser(), afterLoginComponent, loginProvider);
	}

	public LoginPanel(User user, JComponent afterLoginComponent,
			LoginProvider loginProvider) {
		super(user);

		this.afterLoginComponent = afterLoginComponent;
		this.loginProvider = loginProvider;
		addLayoutComponent(this, this.afterLoginComponent, CARD_AFTER);
		this.txtPassword.requestFocus();
	}

	@Override
	protected void createControls(JPanel mainPanel,
			DataBinding<User> dataBinding, ValidationBinding validationBinding) {
		this.loginComponent = createLoginComponent();
		this.layout = new CardLayout();
		setLayout(this.layout);
		addLayoutComponent(this, this.loginComponent, CARD_LOGIN);
		showCard(CARD_LOGIN);
	}

	private void addLayoutComponent(JPanel parent, JComponent component,
			String id) {
		this.layout.addLayoutComponent(component, id);
		parent.add(component);
	}

	private void showCard(String id) {
		this.layout.show(this, id);
		this.layout.layoutContainer(this);
	}

	private JComponent createLoginComponent() {
		JPanel basePanel = new JPanel();
		basePanel.setLayout(new CenterLayout());
		basePanel.add(createLoginGroup());
		return basePanel;
	}

	protected JComponent createLoginGroup() {
		JPanel group = new JPanel();
		group.setBorder(BorderFactory.createTitledBorder(CommonMessages
				.getString("Login.loginGroup")));
		group.setLayout(new GridBagLayout());
		group.setPreferredSize(new Dimension(300, 150));
		int x = 0;
		int y = 0;
		createLabel(group, CommonMessages.getString("Login.login"), x, y++);
		createLabel(group, CommonMessages.getString("Login.password"), x, y++);

		x++;
		y = 0;
		createText(group, "Login", x, y++);
		this.txtPassword = createPassword(group, "Password", x, y++);
		this.txtPassword.addKeyListener(new KeyAdapter() {
			@Override
			public void keyTyped(KeyEvent e) {
				if (e.getKeyChar() == 10) {
					performLogin();
				}
			}
		});
		group.add(createButtonBar(), GridBagUtil.textFieldDefaults(x, y + 1));

		return group;
	}

	protected Component createButtonBar() {
		JPanel parent = new JPanel();
		parent.setLayout(new BorderLayout());

		int buttonSpacing = 5;

		JPanel buttonPanel = new JPanel();
		buttonPanel
				.setLayout(new GridLayout(1, 0, buttonSpacing, buttonSpacing));
		buttonPanel.setBorder(BorderFactory.createEmptyBorder(buttonSpacing,
				buttonSpacing, buttonSpacing, buttonSpacing));
		createButtons(buttonPanel);

		parent.add(new JLabel(), BorderLayout.CENTER);
		parent.add(buttonPanel, BorderLayout.EAST);

		return parent;
	}

	protected void createButtons(JPanel buttonPanel) {
		JButton button;

		button = new JButton(CommonMessages.getString("Dialog.login")); //$NON-NLS-1$
		button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				performLogin();
			}
		});
		button.setMinimumSize(SwingConstants.BUTTON_MIN_SIZE);
		buttonPanel.add(button);
	}

	protected void performLogin() {
		getDataBinding().updateModels();
		User user = getModel();
		if (this.loginProvider.login(user.getLogin(), user.getPassword())) {
			performSuccessfulLogin();
		} else {
			performUnsuccessfulLogin();
		}
	}

	protected void performSuccessfulLogin() {
		showCard(CARD_AFTER);
	}

	protected void performUnsuccessfulLogin() {
		getDataBinding().updateModels();
		ErrorUtil.showError(this,
				CommonMessages.getString("Login.wrongLoginData"));
		resetPasswordField();
	}

	public void performLogout() {
		showCard(CARD_LOGIN);
		resetPasswordField();
	}

	private void resetPasswordField() {
		getModel().setPassword(null);
		getDataBinding().updateTargets();
		this.txtPassword.requestFocus();
	}

	public JComponent getAfterLoginComponent() {
		return this.afterLoginComponent;
	}

	public LoginProvider getLoginProvider() {
		return this.loginProvider;
	}

	/*
	 * 
	 */

	public static User createDefaultUser() {
		User user = new User();
		user.login = CommonMessages.getString("Login.defaultLogin");
		return user;
	}

	public static class User {

		private String login;
		private char[] password;

		public String getLogin() {
			return this.login;
		}

		public void setLogin(String login) {
			this.login = login;
		}

		public char[] getPassword() {
			return this.password;
		}

		public void setPassword(char[] password) {
			this.password = password;
		}

		@Override
		public String toString() {
			return this.login + " = "
					+ (this.password == null ? "" : new String(this.password));
		}

	}

	/*
	 * 
	 */

	public static interface LoginProvider {

		boolean login(String login, char[] password);
	}

}
